home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
machserver
/
1.098
/
fspdev
/
fspdevSrv.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-06-26
|
84KB
|
2,534 lines
/*
* fspdevSrv.c --
*
* This file contains routines directly related to the request-response
* protocol between the client and server processes. Routines that
* setup state, i.e. the SrvOpen, CltOpen, and migration routines,
* are in fsPdevSetup.c. Routines for the Control stream are
* found in fsPdevControl.c
*
* Operations on a pseudo-device are forwarded to a user-level server
* process using a "request-response" protocol.
* The server process declares a request buffer (and optionally a read
* ahead buffer) in its address space. The kernel puts requests, which
* are generated when a client does an operation on the pseudo stream,
* into the request buffer directly. The server learns of new requests
* by reading messages from the server stream that contain offsets within
* the request buffer. Write requests may not require a response from
* the server. Instead the kernel just puts the request into the buffer
* and returns to the client. This allows many requests to be buffered
* before a context switch to the server process. Similarly,
* the server can put read data into the read ahead buffer for a pseudo
* stream. In this case a client's read will be satisfied from the
* buffer and the server won't be contacted.
*
* Copyright 1987, 1988 Regents of the University of California
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: /sprite/src/kernel/fspdev/RCS/fspdevSrv.c,v 9.7 91/06/26 01:06:56 mottsmth Exp $ SPRITE (Berkeley)";
#endif not lint
#include <status.h>
#include <sprite.h>
#include <fs.h>
#include <fsutil.h>
#include <fsNameOps.h>
#include <fsio.h>
#include <fsconsist.h>
#include <fsioLock.h>
#include <fsdm.h>
#include <fsStat.h>
#include <fspdev.h>
#include <proc.h>
#include <rpc.h>
#include <string.h>
/*
* Prevent tracing by defining CLEAN here before this next include
*/
#undef CLEAN
#include <fspdevInt.h>
#include <dev/pfs.h>
/*
* Access to Fspdev_ServerIOHandle is monitored to serialize access to
* the request-response communication channel.
*/
#define LOCKPTR (&pdevHandlePtr->lock)
/*
* Flags for a pseudo device handle.
* PDEV_SETUP The server has set up state for the stream.
* PDEV_BUSY Set during request-response to ensure that
* only one client process is using the stream.
* PDEV_REPLY_READY The server gave us a reply
* PDEV_REPLY_FAILED There was some problem (usually with copy in)
* getting the reply from the server.
* PDEV_SERVER_GONE Set after the server closes its stream.
* PDEV_READ_BUF_EMPTY When set there is no data in the read ahead
* buf.
* PDEV_SERVER_KNOWS_IT Set after the server has done a read and been
* told that we too think the read ahead buffer
* is empty. This synchronization simplifies
* things; there is no wrapping; there is no
* confusion about full vs. empty.
* PDEV_READ_PTRS_CHANGED Set when we have used data from the read
* ahead buffer. This makes the server stream
* selectable so it will find out about the
* new pointers.
* PDEV_WRITE_BEHIND Write-behind is enabled for this stream.
* PDEV_NO_BIG_WRITES Causes writes larger than will fit into
* the request buffer to fail. This is to
* support UDP socket semantics, sigh.
* PDEV_NAMING Set on the naming request-response channel.
* PDEV_REQUEST_ABORTED Set when the client aborts during a request-
* response transaction so we ignore the next rply.
* FS_USER_IN/OUT These flags are borrowed from the stream flags
* and indicate which buffers are in user space
*/
#define PDEV_SETUP 0x0001
#define PDEV_BUSY 0x0002
#define PDEV_REPLY_READY 0x0004
#define PDEV_REPLY_FAILED 0x0008
#define PDEV_SERVER_GONE 0x0010
#define PDEV_READ_BUF_EMPTY 0x0020
#define PDEV_SERVER_KNOWS_IT 0x0040
#define PDEV_READ_PTRS_CHANGED 0x0080
#define PDEV_WRITE_BEHIND 0x0100
#define PDEV_NO_BIG_WRITES 0x0200
#define PDEV_NAMING 0x0400
#define PDEV_REQUEST_ABORTED 0x0800
/*resrv FS_USER_IN 0x8000 */
/*resrv FS_USER_OUT 0x800000 */
/*
* Forward declarations.
*/
static ReturnStatus RequestResponse _ARGS_((
Fspdev_ServerIOHandle *pdevHandlePtr,
int hdrSize, register Pdev_RequestHdr *requestHdrPtr, int inputSize,
Address inputBuf, int replySize, Address replyBuf,
Fs_IOReply *ioReplyPtr, Sync_RemoteWaiter *waitPtr));
static void PdevClientWakeup _ARGS_((Fspdev_ServerIOHandle *pdevHandlePtr));
static void PdevClientNotify _ARGS_((Fspdev_ServerIOHandle *pdevHandlePtr));
/*
*----------------------------------------------------------------------
*
* RequestResponse --
*
* The general request-response protocol between a client's pseudo
* stream and the server stream. This passes a request to the server
* that specifies the command and parameters, the size of the input data
* and the max size of the reply. Then, if a reply is needed, this blocks
* the client until the server responds.
*
* Results:
* An error code from the server, or from errors on the vm copies.
*
* Side effects:
* The server process is locked while we copy the request into the
* buffer in the server's address space. The firstByte and lastByte
* offsets into the pseudo stream request buffer are updated to
* reflect the new request. The corresponding server stream is
* marked FS_READABLE.
*
*----------------------------------------------------------------------
*/
INTERNAL static ReturnStatus
RequestResponse(pdevHandlePtr, hdrSize, requestHdrPtr, inputSize, inputBuf,
replySize, replyBuf, ioReplyPtr, waitPtr)
register Fspdev_ServerIOHandle *pdevHandlePtr; /* Caller should lock this
* with the monitor lock. */
int hdrSize; /* Either sizeof(Pdev_Request) or
* sizeof(Pfs_Request) */
register Pdev_RequestHdr *requestHdrPtr; /* Caller fills in the
* command and parameter parts. The
* sizes are filled in here. */
int inputSize; /* Size of input buffer. */
Address inputBuf; /* Inputs of the remote command. */
int replySize; /* Size of output buffer. 0 means
* no reply data expected. If the
* operation is PDEV_WRITE_ASYNC then no
* reply is wanted and this is ignored*/
Address replyBuf; /* Results of the remote command. */
Fs_IOReply *ioReplyPtr; /* Amount transferred and signal to
* return. (May be NIL if not needed.)*/
Sync_RemoteWaiter *waitPtr; /* Client process info for waiting.
* Only needed for read & write. */
{
register ReturnStatus status;
Proc_ControlBlock *serverProcPtr; /* For VM copy operations */
register int firstByte; /* Offset into request buffer */
register int lastByte; /* Offset into request buffer */
int room; /* Room available in req. buf.*/
int savedLastByte; /* For error recovery */
int savedFirstByte; /* ditto */
if (ioReplyPtr != (Fs_IOReply *) NIL) {
ioReplyPtr->length = 0;
}
if ((pdevHandlePtr == (Fspdev_ServerIOHandle *)NIL) ||
(pdevHandlePtr->flags & PDEV_SERVER_GONE)) {
return(DEV_OFFLINE);
} else if ((pdevHandlePtr->flags & PDEV_SETUP) == 0) {
panic( "RequestResponse: connection not set up\n");
}
/*
* See if we have to switch to a new request buffer. This is needed
* to support UDP, which wants to set a maximum write size. The max
* is implemented by letting the UDP server change the buffer size and
* setting the property that writes larger than the buffer fail. We
* wait and switch after the request buffer empties.
*/
firstByte = pdevHandlePtr->requestBuf.firstByte;
lastByte = pdevHandlePtr->requestBuf.lastByte;
if ((pdevHandlePtr->nextRequestBuffer != (Address)NIL) &&
((firstByte > lastByte) || (firstByte == -1))) {
#ifndef CLEAN
printf( "Switching to request buffer at 0x%x\n",
pdevHandlePtr->nextRequestBuffer);
#endif
pdevHandlePtr->requestBuf.data = pdevHandlePtr->nextRequestBuffer;
pdevHandlePtr->requestBuf.size = pdevHandlePtr->nextRequestBufSize;
pdevHandlePtr->nextRequestBuffer = (Address)NIL;
firstByte = -1;
}
/*
* FORMAT THE REQUEST HEADER. Note that the complete message size is
* rounded up so subsequent messages start on word boundaries.
*/
if (hdrSize == sizeof(Pdev_Request)) {
requestHdrPtr->magic = PDEV_REQUEST_MAGIC;
} else if (hdrSize == sizeof(Pfs_Request)) {
requestHdrPtr->magic = PFS_REQUEST_MAGIC;
} else {
panic( "RequestResponse: bad hdr size\n");
}
requestHdrPtr->requestSize = inputSize;
requestHdrPtr->replySize = replySize;
requestHdrPtr->messageSize = hdrSize +
((inputSize + sizeof(int) - 1) / sizeof(int)) * sizeof(int);
if (pdevHandlePtr->requestBuf.size < requestHdrPtr->messageSize) {
printf("RequestResponse: request too large (%d > %d)\n",
requestHdrPtr->messageSize, pdevHandlePtr->requestBuf.size);
return(GEN_INVALID_ARG);
}
PDEV_REQUEST_PRINT(&pdevHandlePtr->hdr.fileID, requestHdrPtr);
PDEV_REQUEST(&pdevHandlePtr->hdr.fileID, requestHdrPtr);
/*
* PUT THE REQUEST INTO THE REQUEST BUFFER.
* We assume that our caller will not give us a request that can't all
* fit into the buffer. However, if the buffer is not empty enough we
* wait for the server to catch up with us. (Things could be optimized
* perhaps to wait for just enough room. To be real clever you have
* to be careful when you reset the firstByte so that the server only
* notices after it has caught up with everything at the end of the
* buffer. To keep things simple we just wait for the server to catch up
* completely if we can't fit this request in.)
*/
savedFirstByte = firstByte;
savedLastByte = lastByte;
if (firstByte > lastByte || firstByte == -1) {
/*
* Buffer has emptied.
*/
firstByte = 0;
pdevHandlePtr->requestBuf.firstByte = firstByte;
lastByte = requestHdrPtr->messageSize - 1;
} else {
room = pdevHandlePtr->requestBuf.size - (lastByte + 1);
if (room < requestHdrPtr->messageSize) {
/*
* There is no room left at the end of the buffer.
* We wait and then put the request at the beginning.
*/
while (pdevHandlePtr->requestBuf.firstByte <
pdevHandlePtr->requestBuf.lastByte) {
DBG_PRINT( (" (catch up) ") );
if (Sync_Wait(&pdevHandlePtr->caughtUp, TRUE)) {
status = GEN_ABORTED_BY_SIGNAL;
goto failure;
} else if (pdevHandlePtr->flags & PDEV_SERVER_GONE) {
status = DEV_OFFLINE;
goto failure;
}
}
savedFirstByte = -1;
firstByte = 0;
pdevHandlePtr->requestBuf.firstByte = firstByte;
lastByte = requestHdrPtr->messageSize - 1;
} else {
/*
* Append the message to the other requests.
*/
firstByte = lastByte + 1;
lastByte += requestHdrPtr->messageSize;
}
}
pdevHandlePtr->requestBuf.lastByte = lastByte;
DBG_PRINT( (" first %d last %d\n", firstByte, lastByte) );
pdevHandlePtr->operation = requestHdrPtr->operation;
if (pdevHandlePtr->operation == PFS_OPEN) {
/*
* We have to snarf up the hostID of the client doing the open
* so the new pseudo-device connection that may be set up as
* a side effect of PFS_OPEN is set up right. The useFlags are
* needed to initialize the new client stream right.
*/
register Pfs_Request *pfsRequestPtr = (Pfs_Request *)requestHdrPtr;
pdevHandlePtr->open.clientID = pfsRequestPtr->param.open.clientID;
pdevHandlePtr->open.useFlags = pfsRequestPtr->param.open.useFlags;
pdevHandlePtr->open.name = (char *)inputBuf;
}
/*
* COPY REQUEST AND DATA.
* Copy the request and data out into the server's request buffer.
* We map to a proc table pointer for the server which has a side
* effect of locking down the server process so it can't disappear.
*/
serverProcPtr = Proc_LockPID(pdevHandlePtr->serverPID);
if (serverProcPtr == (Proc_ControlBlock *)NIL) {
status = DEV_OFFLINE;
goto failure;
}
status = Vm_CopyOutProc(hdrSize, (Address)requestHdrPtr, TRUE,
serverProcPtr, (Address)
&pdevHandlePtr->requestBuf.data[firstByte]);
if (status == SUCCESS) {
firstByte += hdrSize;
if (inputSize > 0) {
status = Vm_CopyOutProc(inputSize, inputBuf,
(pdevHandlePtr->flags & FS_USER_IN) == 0, serverProcPtr,
(Address)&pdevHandlePtr->requestBuf.data[firstByte]);
}
}
Proc_Unlock(serverProcPtr);
if (status != SUCCESS) {
/*
* Either the message header or data couldn't get copied out.
* Reset the buffer pointers so the bad request isn't seen.
*/
pdevHandlePtr->requestBuf.firstByte = savedFirstByte;
pdevHandlePtr->requestBuf.lastByte = savedLastByte;
goto failure;
}
/*
* POKE THE SERVER so it can read the new pointer values.
* This is done here even if write-behind is enabled, even though our
* scheduler tends to wake up the server too soon.
* Although it is possible to put a notify in about 3 other places
* to catch cases where the client does a write-behind and then waits,
* not all clients are clever enough to use select. That solution results
* in cases where a write can linger a long time in the request buffer.
* (cat /dev/syslog in a tx window is a good test case.)
*/
Fsutil_FastWaitListNotify(&pdevHandlePtr->srvReadWaitList);
if (pdevHandlePtr->operation != PDEV_WRITE_ASYNC) {
/*
* WAIT FOR A REPLY.
* We save the client's reply buffer address and processID in the
* stream state so the kernel can copy the reply directly from
* the server's address space to the client's when the server
* makes the IOC_PDEV_REPLY IOControl.
*/
pdevHandlePtr->replyBuf = replyBuf;
pdevHandlePtr->replySize = replySize;
pdevHandlePtr->clientPID = (Proc_GetEffectiveProc())->processID;
if (waitPtr != (Sync_RemoteWaiter *)NIL) {
pdevHandlePtr->clientWait = *waitPtr;
}
pdevHandlePtr->flags &= ~PDEV_REPLY_READY;
while ((pdevHandlePtr->flags & PDEV_REPLY_READY) == 0) {
if (Sync_Wait(&pdevHandlePtr->replyReady, TRUE)) {
if ((pdevHandlePtr->flags & PDEV_REPLY_READY) == 0) {
/*
* Client aborted before the server completed the request.
*/
pdevHandlePtr->flags |= PDEV_REQUEST_ABORTED;
}
status = GEN_ABORTED_BY_SIGNAL;
goto failure;
} else if (pdevHandlePtr->flags &
(PDEV_REPLY_FAILED|PDEV_SERVER_GONE)) {
status = DEV_OFFLINE;
goto failure;
}
}
if (ioReplyPtr != (Fs_IOReply *) NIL) {
ioReplyPtr->length = pdevHandlePtr->reply.replySize;
ioReplyPtr->signal = pdevHandlePtr->reply.signal;
ioReplyPtr->code = pdevHandlePtr->reply.code;
}
} else {
pdevHandlePtr->reply.status = SUCCESS;
}
failure:
pdevHandlePtr->replyBuf = (char *)NIL;
pdevHandlePtr->replySize = 0;
if (status == SUCCESS) {
status = pdevHandlePtr->reply.status;
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* FspdevServerStreamCreate --
*
* Set up the stream state for a server's private channel to a client.
* This creates a Fspdev_ServerIOHandle that has all the state for the
* connection to the client.
*
* Results:
* A pointer to the I/O handle created. The handle is locked.
* NIL is returned if a handle under the fileID already existed.
*
* Side effects:
* The I/O handle for this connection between a client and the server
* is installed and initialized.
*
*----------------------------------------------------------------------
*/
Fspdev_ServerIOHandle *
FspdevServerStreamCreate(ioFileIDPtr, name, naming)
Fs_FileID *ioFileIDPtr; /* File ID used for pseudo stream handle */
char *name; /* File name for error messages */
Boolean naming; /* True if this is a naming stream for a pfs */
{
Fs_HandleHeader *hdrPtr;
register Fspdev_ServerIOHandle *pdevHandlePtr;
Boolean found;
ioFileIDPtr->type = FSIO_SERVER_STREAM;
found = Fsutil_HandleInstall(ioFileIDPtr, sizeof(Fspdev_ServerIOHandle), name,
FALSE, &hdrPtr);
pdevHandlePtr = (Fspdev_ServerIOHandle *)hdrPtr;
if (found) {
printf("ServerStreamCreate, found handle <%x,%x,%x>\n",
hdrPtr->fileID.serverID, hdrPtr->fileID.major,
hdrPtr->fileID.minor);
Fsutil_HandleRelease(pdevHandlePtr, TRUE);
return((Fspdev_ServerIOHandle *)NIL);
}
fs_Stats.object.pseudoStreams++;
DBG_PRINT( ("ServerStreamOpen <%d,%x,%x>\n",
ioFileIDPtr->serverID, ioFileIDPtr->major, ioFileIDPtr->minor) );
/*
* Initialize the state for the pseudo stream. Remember that
* the request and read ahead buffers for the pseudo-stream are set up
* via IOControls by the server process later. In the meantime we
* pretend the connection is busy to lock out requests.
*/
Sync_LockInitDynamic(&pdevHandlePtr->lock, "Fs:pdevLock");
pdevHandlePtr->flags = PDEV_BUSY;
if (naming) {
pdevHandlePtr->flags |= PDEV_NAMING;
}
pdevHandlePtr->selectBits = 0;
pdevHandlePtr->serverPID = (Proc_PID)NIL;
pdevHandlePtr->clientPID = (Proc_PID)NIL;
pdevHandlePtr->requestBuf.data = (Address)NIL;
pdevHandlePtr->requestBuf.firstByte = -1;
pdevHandlePtr->requestBuf.lastByte = -1;
pdevHandlePtr->requestBuf.size = 0;
pdevHandlePtr->nextRequestBuffer = (Address)NIL;
pdevHandlePtr->readBuf.data = (Address)NIL;
pdevHandlePtr->readBuf.firstByte = -1;
pdevHandlePtr->readBuf.lastByte = -1;
pdevHandlePtr->readBuf.size = 0;
pdevHandlePtr->operation = 0;
pdevHandlePtr->reply.status = NIL;
pdevHandlePtr->reply.replySize = 0;
pdevHandlePtr->replyBuf = (Address)NIL;
pdevHandlePtr->setup.waiting = 0;
pdevHandlePtr->access.waiting = 0;
pdevHandlePtr->caughtUp.waiting = 0;
pdevHandlePtr->replyReady.waiting = 0;
List_Init(&pdevHandlePtr->srvReadWaitList);
pdevHandlePtr->clientWait.pid = NIL;
pdevHandlePtr->clientWait.hostID = NIL;
pdevHandlePtr->clientWait.waitToken = NIL;
List_Init(&pdevHandlePtr->cltReadWaitList);
List_Init(&pdevHandlePtr->cltWriteWaitList);
List_Init(&pdevHandlePtr->cltExceptWaitList);
pdevHandlePtr->ctrlHandlePtr = (Fspdev_ControlIOHandle *)NIL;
pdevHandlePtr->userLevelID = *ioFileIDPtr;
pdevHandlePtr->open.clientID = NIL;
pdevHandlePtr->open.useFlags = 0;
pdevHandlePtr->open.name = (char *)NIL;
return(pdevHandlePtr);
}
/*
*----------------------------------------------------------------------
*
* FspdevServerStreamSelect --
*
* Select a server's request/response stream. This returns
* FS_READABLE in the outFlags if there is data in the
* server's request buffer. The next read on the server stream
* will return the current pointers into the buffer.
*
* Results:
* SUCCESS
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ReturnStatus
FspdevServerStreamSelect(hdrPtr, waitPtr, readPtr, writePtr, exceptPtr)
Fs_HandleHeader *hdrPtr; /* Handle on device to select */
Sync_RemoteWaiter *waitPtr; /* Process info for remote waiting */
int *readPtr; /* Bit to clear if non-readable */
int *writePtr; /* Bit to clear if non-writeable */
int *exceptPtr; /* Bit to clear if non-exceptable */
{
register Fspdev_ServerIOHandle *pdevHandlePtr = (Fspdev_ServerIOHandle *)hdrPtr;
LOCK_MONITOR;
if (*readPtr) {
if (((pdevHandlePtr->flags & PDEV_READ_PTRS_CHANGED) == 0) &&
((pdevHandlePtr->requestBuf.firstByte == -1) ||
(pdevHandlePtr->requestBuf.firstByte >=
pdevHandlePtr->requestBuf.lastByte))) {
*readPtr = 0;
if (waitPtr != (Sync_RemoteWaiter *)NIL) {
Fsutil_FastWaitListInsert(&pdevHandlePtr->srvReadWaitList, waitPtr);
}
}
}
*writePtr = 0;
*exceptPtr = 0;
UNLOCK_MONITOR;
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* FspdevServerStreamRead --
*
* When the server reads on a server stream it is looking for a
* message containing pointers into the request buffer that's
* in its address spapce. This routine returns those values.
*
* Results:
* SUCCESS unless all clients have gone away.
*
* Side effects:
* The buffer is filled a Pdev_BufPtrs structure.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
FspdevServerStreamRead(streamPtr, readPtr, waitPtr, replyPtr)
register Fs_Stream *streamPtr; /* Stream to read from. */
Fs_IOParam *readPtr; /* Read parameter block. */
Sync_RemoteWaiter *waitPtr; /* Process info for remote waiting */
Fs_IOReply *replyPtr; /* Signal to return, if any,
* plus the amount read. */
{
register Fspdev_ServerIOHandle *pdevHandlePtr =
(Fspdev_ServerIOHandle *)streamPtr->ioHandlePtr;
register ReturnStatus status;
Pdev_BufPtrs bufPtrs;
register int reqFirstByte, reqLastByte;
LOCK_MONITOR;
/*
* The server stream is readable only if there are requests in the
* request buffer or if the read ahead buffers have changed since
* the last time the server did a read.
*/
reqFirstByte = pdevHandlePtr->requestBuf.firstByte;
reqLastByte = pdevHandlePtr->requestBuf.lastByte;
if (reqFirstByte > pdevHandlePtr->requestBuf.size ||
reqLastByte > pdevHandlePtr->requestBuf.size) {
panic("PdevServerRead, pointers inconsistent\n");
UNLOCK_MONITOR;
return(GEN_INVALID_ARG);
}
if (((pdevHandlePtr->flags & PDEV_READ_PTRS_CHANGED) == 0) &&
((reqFirstByte == -1) || (reqFirstByte > reqLastByte))) {
status = FS_WOULD_BLOCK;
replyPtr->length = 0;
Fsutil_FastWaitListInsert(&pdevHandlePtr->srvReadWaitList, waitPtr);
PDEV_TRACE(&pdevHandlePtr->hdr.fileID, PDEVT_SRV_READ_WAIT);
} else {
/*
* Copy the current pointers out to the server. We include the
* server's address of the request buffer to support changing
* the request buffer after requests have started to flow.
*/
PDEV_TRACE(&pdevHandlePtr->hdr.fileID, PDEVT_SRV_READ);
bufPtrs.magic = PDEV_BUF_PTR_MAGIC;
bufPtrs.requestAddr = pdevHandlePtr->requestBuf.data;
if ((reqFirstByte == -1) || (reqFirstByte > reqLastByte)) {
/*
* Request buffer is empty.
*/
bufPtrs.requestFirstByte = -1;
bufPtrs.requestLastByte = -1;
} else {
bufPtrs.requestFirstByte = reqFirstByte;
bufPtrs.requestLastByte = reqLastByte;
}
/*
* The read ahead buffer is filled by the server until there is
* no room left. Only after the kernel has emptied the
* read ahead buffer will the server start filling it again.
* We use the PDEV_SERVER_KNOWS_IT state bit to know when to
* expect new pointer values after the buffer empties.
*/
if (pdevHandlePtr->flags & PDEV_READ_BUF_EMPTY) {
bufPtrs.readLastByte = -1;
bufPtrs.readFirstByte = -1;
pdevHandlePtr->flags |= PDEV_SERVER_KNOWS_IT;
} else {
bufPtrs.readFirstByte = pdevHandlePtr->readBuf.firstByte;
bufPtrs.readLastByte = pdevHandlePtr->readBuf.lastByte;
}
pdevHandlePtr->flags &= ~PDEV_READ_PTRS_CHANGED;
status = Vm_CopyOut(sizeof(Pdev_BufPtrs), (Address)&bufPtrs,
readPtr->buffer);
replyPtr->length = sizeof(Pdev_BufPtrs);
DBG_PRINT( ("READ %x,%x req %d:%d read %d:%d\n",
pdevHandlePtr->hdr.fileID.major,
pdevHandlePtr->hdr.fileID.minor,
pdevHandlePtr->requestBuf.firstByte,
pdevHandlePtr->requestBuf.lastByte,
pdevHandlePtr->readBuf.firstByte,
pdevHandlePtr->readBuf.lastByte) );
}
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* FspdevServerStreamIOControl --
*
* IOControls for the server's stream. The server process uses this
* to manipulate the request and read ahead buffers in its address
* space. It delcares them, and queries and sets pointers into them.
* The server also replies to requests here, and notifies the kernel
* when the pseudo-device is selectable.
*
* Results:
* SUCCESS if all went well, otherwise a status from a Vm operation
* or a consistency check.
*
* Side effects:
* This is the main entry point for the server process to control
* the pseudo-device connection to its clients. The side effects
* depend on the I/O control.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ENTRY ReturnStatus
FspdevServerStreamIOControl(streamPtr, ioctlPtr, replyPtr)
Fs_Stream *streamPtr; /* Stream to server handle. */
Fs_IOCParam *ioctlPtr; /* Standard I/O Control parameter block */
Fs_IOReply *replyPtr; /* Output buffer length and signal to return */
{
ReturnStatus status = SUCCESS;
register Fspdev_ServerIOHandle *pdevHandlePtr =
(Fspdev_ServerIOHandle *)streamPtr->ioHandlePtr;
LOCK_MONITOR;
if (ioctlPtr->format != mach_Format) {
panic("FsServerStreamIOControl: wrong format\n");
}
switch (ioctlPtr->command) {
case IOC_PDEV_SET_BUF: {
/*
* The server is declaring the buffer space used for requests
* and (optionally) for read ahead.
* To support UDP socket semantics, we let the server change
* the request buffer after things have already started up.
* (This is sort of a pain, as we have to let the current
* request buffer empty before switching.)
*
* Side effects:
* Set the request, and optionally the read-ahead,
* buffer pointers. The setup condition is notified
* to let the client's open transaction begin.
*/
register Pdev_SetBufArgs *argPtr =
(Pdev_SetBufArgs *)ioctlPtr->inBuffer;
register int extraBytes;
if (ioctlPtr->inBufSize != sizeof(Pdev_SetBufArgs)) {
status = GEN_INVALID_ARG;
} else if ((pdevHandlePtr->flags & PDEV_SETUP) == 0) {
/*
* Normal case, first time initialization. Make sure the
* buffers are word aligned.
*/
pdevHandlePtr->requestBuf.size = argPtr->requestBufSize;
pdevHandlePtr->requestBuf.data = argPtr->requestBufAddr;
extraBytes = (unsigned int)argPtr->requestBufAddr &
(sizeof(int) - 1);
if (extraBytes) {
pdevHandlePtr->requestBuf.data += sizeof(int) - extraBytes;
pdevHandlePtr->requestBuf.size -= extraBytes;
}
pdevHandlePtr->requestBuf.firstByte = -1;
pdevHandlePtr->requestBuf.lastByte = -1;
if (argPtr->readBufAddr == (Address)NIL ||
argPtr->readBufAddr == (Address)0) {
pdevHandlePtr->readBuf.data = (Address)NIL;
pdevHandlePtr->readBuf.size = 0;
} else {
pdevHandlePtr->readBuf.size = argPtr->readBufSize;
pdevHandlePtr->readBuf.data = argPtr->readBufAddr;
extraBytes = (unsigned int)argPtr->readBufAddr &
(sizeof(int) - 1);
if (extraBytes) {
pdevHandlePtr->readBuf.data += sizeof(int) - extraBytes;
pdevHandlePtr->readBuf.size -= extraBytes;
}
}
pdevHandlePtr->readBuf.firstByte = -1;
pdevHandlePtr->readBuf.lastByte = -1;
pdevHandlePtr->serverPID = ioctlPtr->procID;
/*
* The state has been marked BUSY to simplify waiting in the
* routines that call RequestResponse, so we clear that bit.
*/
pdevHandlePtr->flags &= ~PDEV_BUSY;
pdevHandlePtr->flags |= PDEV_SETUP|PDEV_READ_BUF_EMPTY|
PDEV_SERVER_KNOWS_IT;
Sync_Broadcast(&pdevHandlePtr->access);
} else {
/*
* The server is changing request buffers. We just remember
* the new buffer address and size here, and switch over
* in RequestResponse after the current request buffer empties.
*/
pdevHandlePtr->nextRequestBuffer = argPtr->requestBufAddr;
pdevHandlePtr->nextRequestBufSize = argPtr->requestBufSize;
if (pdevHandlePtr->nextRequestBuffer == (Address)0) {
pdevHandlePtr->nextRequestBuffer = (Address)NIL;
}
}
break;
}
case IOC_PDEV_WRITE_BEHIND: {
/*
* Side effects:
* Set/unset write-behind buffering in the request buffer.
*/
register Boolean writeBehind;
if (ioctlPtr->inBufSize < sizeof(Boolean)) {
status = GEN_INVALID_ARG;
} else {
writeBehind = *(Boolean *)ioctlPtr->inBuffer;
if (writeBehind) {
pdevHandlePtr->flags |= PDEV_WRITE_BEHIND;
} else {
pdevHandlePtr->flags &= ~PDEV_WRITE_BEHIND;
}
}
break;
}
case IOC_PDEV_BIG_WRITES: {
/*
* Side effects:
* Set/unset the client's ability to make large writes.
*/
register Boolean allowLargeWrites;
if (ioctlPtr->inBufSize < sizeof(Boolean)) {
status = GEN_INVALID_ARG;
} else {
allowLargeWrites = *(Boolean *)ioctlPtr->inBuffer;
if (allowLargeWrites) {
pdevHandlePtr->flags &= ~PDEV_NO_BIG_WRITES;
} else {
pdevHandlePtr->flags |= PDEV_NO_BIG_WRITES;
}
}
break;
}
case IOC_PDEV_SET_PTRS: {
/*
* The server is telling us about new pointer values. We only
* pay attention to the requestFirstByte and readLastByte as
* it is our job to modify the other pointers.
*
* Side effects:
* Set requestBuf.firstByte and readBuf.lastByte if
* the server gives us new values (not equal -1).
* We notify waiting clients if the server has
* added read-ahead data.
*/
register Pdev_BufPtrs *argPtr = (Pdev_BufPtrs *)ioctlPtr->inBuffer;
if (ioctlPtr->inBufSize != sizeof(Pdev_BufPtrs)) {
status = GEN_INVALID_ARG;
} else {
/*
* Verify the request buffer pointer. The server may just
* be telling us about read ahead data, in which case we
* shouldn't muck with the request pointers. Otherwise we
* update the request first byte to reflect the processing
* of some requests by the server.
*/
DBG_PRINT( ("SET %x,%x req %d:%d read %d:%d\n",
pdevHandlePtr->hdr.fileID.major,
pdevHandlePtr->hdr.fileID.minor,
argPtr->requestFirstByte, argPtr->requestLastByte,
argPtr->readFirstByte, argPtr->readLastByte) );
if (argPtr->requestFirstByte <=
pdevHandlePtr->requestBuf.size &&
argPtr->requestFirstByte >= 0) {
pdevHandlePtr->requestBuf.firstByte =
argPtr->requestFirstByte;
}
Sync_Broadcast(&pdevHandlePtr->caughtUp);
if ((pdevHandlePtr->readBuf.data == (Address)NIL) ||
(argPtr->readLastByte < 0)) {
/*
* No read ahead info.
*/
break;
}
if (argPtr->readLastByte > pdevHandlePtr->readBuf.size) {
printf("FspdevServerStreamIOControl: set bad readPtr\n");
status = GEN_INVALID_ARG;
break;
}
if ((pdevHandlePtr->flags & PDEV_READ_BUF_EMPTY) == 0) {
/*
* Non-empty buffer. Break out if bad pointer, else
* fall through to code that updates the pointer.
*/
if (argPtr->readLastByte <=
pdevHandlePtr->readBuf.lastByte) {
break; /* No new read ahead data */
}
} else if (pdevHandlePtr->flags & PDEV_SERVER_KNOWS_IT) {
/*
* Empty buffer and the server already knows this.
* We can safely reset firstByte to the beginning.
* The server should rely on this behavior.
*/
if (argPtr->readLastByte >= 0) {
pdevHandlePtr->flags &= ~(PDEV_READ_BUF_EMPTY|
PDEV_SERVER_KNOWS_IT);
pdevHandlePtr->readBuf.firstByte = 0;
} else {
break; /* No new read ahead data */
}
} else {
/*
* We emptied the buffer, but the server added data
* before seeing it was empty. Can't reset firstByte.
*/
if (argPtr->readLastByte >
pdevHandlePtr->readBuf.lastByte) {
pdevHandlePtr->flags &= ~PDEV_READ_BUF_EMPTY;
} else {
break; /* No new read ahead data */
}
}
/*
* We know here that the lastByte pointer indicates
* more data. Otherwise we've broken out.
* Update select state and poke waiting readers.
*/
pdevHandlePtr->readBuf.lastByte = argPtr->readLastByte;
pdevHandlePtr->selectBits |= FS_READABLE;
Fsutil_FastWaitListNotify(&pdevHandlePtr->cltReadWaitList);
}
break;
}
case IOC_PDEV_SMALL_REPLY:
case IOC_PDEV_REPLY: {
/*
* The server is replying to a request.
*
* Side effects:
* Copy the reply from the server to the client.
* Put the client on wait lists, if appropriate.
* Notify the replyReady condition, and lastly
* notify waiting clients about new select state.
*/
register Pdev_Reply *srvReplyPtr = (Pdev_Reply *)ioctlPtr->inBuffer;
if (ioctlPtr->inBufSize < sizeof(Pdev_Reply)) {
/*
* inBuffer must be at least as big as Pdev_Reply.
* It will be larger during PDEV_SMALL_REPLY which
* includes a small amount of embedded data.
*/
status = FS_INVALID_ARG;
pdevHandlePtr->flags |= PDEV_REPLY_READY|PDEV_REPLY_FAILED;
} else if (pdevHandlePtr->flags & PDEV_REQUEST_ABORTED) {
/*
* Client aborted the request from its end.
* Do nothing but grab the select bits.
*/
pdevHandlePtr->flags &= ~PDEV_REQUEST_ABORTED;
pdevHandlePtr->selectBits = srvReplyPtr->selectBits;
} else {
/*
* Copy current reply in to pdev state.
*/
pdevHandlePtr->reply = *srvReplyPtr;
if (srvReplyPtr->replySize > 0) {
register Proc_ControlBlock *clientProcPtr;
if (pdevHandlePtr->replyBuf == (char *)NIL) {
printf("PdevReply: unwanted reply data <%s>\n",
Fsutil_HandleName(pdevHandlePtr));
goto noReplyData;
}
if (srvReplyPtr->replySize > pdevHandlePtr->replySize) {
printf("PdevReply: extra reply data (%d > %d) <%s>\n",
srvReplyPtr->replySize, pdevHandlePtr->replySize,
Fsutil_HandleName(pdevHandlePtr));
srvReplyPtr->replySize = pdevHandlePtr->replySize;
}
/*
* Copy the reply into the waiting buffers.
*/
if ((ioctlPtr->command == IOC_PDEV_SMALL_REPLY) &&
((srvReplyPtr->replySize > PDEV_SMALL_DATA_LIMIT) ||
(ioctlPtr->inBufSize <
sizeof(Pdev_Reply) + srvReplyPtr->replySize))){
status = GEN_INVALID_ARG;
} else {
if ((pdevHandlePtr->flags & FS_USER_OUT) == 0) {
/*
* Reply buffer in the kernel.
*/
if (ioctlPtr->command == IOC_PDEV_SMALL_REPLY) {
bcopy(((Pdev_ReplyData *)srvReplyPtr)->data,
pdevHandlePtr->replyBuf,
srvReplyPtr->replySize);
} else {
status = Vm_CopyIn(srvReplyPtr->replySize,
srvReplyPtr->replyBuf,
pdevHandlePtr->replyBuf);
}
} else {
clientProcPtr = Proc_LockPID(pdevHandlePtr->clientPID);
/*
* Reply buffer in the client's address space.
* Do a cross-address-space copy.
*/
if (clientProcPtr == (Proc_ControlBlock *)NIL) {
status = FS_BROKEN_PIPE;
} else {
if (ioctlPtr->command == IOC_PDEV_SMALL_REPLY) {
status =
Vm_CopyOutProc(srvReplyPtr->replySize,
((Pdev_ReplyData *)srvReplyPtr)->data,
TRUE, clientProcPtr,
pdevHandlePtr->replyBuf);
} else {
status =
Vm_CopyOutProc(srvReplyPtr->replySize,
srvReplyPtr->replyBuf, FALSE,
clientProcPtr, pdevHandlePtr->replyBuf);
}
Proc_Unlock(clientProcPtr);
}
}
}
if (status != SUCCESS) {
pdevHandlePtr->flags |= PDEV_REPLY_FAILED;
}
}
noReplyData:
PDEV_REPLY(&pdevHandlePtr->hdr.fileID, srvReplyPtr);
if (srvReplyPtr->status == FS_WOULD_BLOCK) {
/*
* Put the client process on the appropriate wait list.
*/
if (pdevHandlePtr->operation == PDEV_READ) {
Fsutil_FastWaitListInsert(&pdevHandlePtr->cltReadWaitList,
&pdevHandlePtr->clientWait);
} else if (pdevHandlePtr->operation == PDEV_WRITE) {
Fsutil_FastWaitListInsert(&pdevHandlePtr->cltWriteWaitList,
&pdevHandlePtr->clientWait);
}
}
pdevHandlePtr->flags |= PDEV_REPLY_READY;
pdevHandlePtr->selectBits = srvReplyPtr->selectBits;
}
Sync_Broadcast(&pdevHandlePtr->replyReady);
PdevClientNotify(pdevHandlePtr);
break;
}
case IOC_PDEV_READY:
/*
* Master has made the device ready. The inBuffer contains
* new select bits.
*
* Side effects:
* Notify waiting clients.
*/
if (ioctlPtr->inBufSize != sizeof(int)) {
status = FS_INVALID_ARG;
} else {
/*
* Update the select state of the pseudo-device and
* wake up any clients waiting on their pseudo-stream.
*/
pdevHandlePtr->selectBits = *(int *)ioctlPtr->inBuffer;
PdevClientNotify(pdevHandlePtr);
}
break;
case IOC_PDEV_SIGNAL_OWNER: {
/*
* The server wants to signal the process (group) that owns
* the device. This is used, for example, to implement processing
* of interrupt characters by the TTY driver.
*/
status = FspdevSignalOwner(pdevHandlePtr->ctrlHandlePtr, ioctlPtr);
break;
}
case IOC_PFS_OPEN: {
/*
* A pseudo-filesystem server is replying to an open request by
* asking us to open a pseudo-device connection to the client.
* It gives us a Fs_FileID for its own identification of the
* connection. The connection is set up here. A user-level
* streamID is generated for the server process and returned.
* To finish up, we set up the Fs_OpenResults that the waiting
* client has to RequestResponse. This includes a fileID so
* the client process can fetch its half of the connection.
*/
int newStreamID; /* For the server */
Fs_OpenResults openResults; /* For the client */
if (ioctlPtr->inBufSize < sizeof(Fs_FileID) ||
ioctlPtr->outBufSize < sizeof(int)) {
status = GEN_INVALID_ARG;
} else if (pdevHandlePtr->flags & PDEV_REQUEST_ABORTED) {
pdevHandlePtr->flags &= ~PDEV_REQUEST_ABORTED;
PdevClientNotify(pdevHandlePtr);
break;
} else {
newStreamID = FspdevPfsOpenConnection(pdevHandlePtr,
(Fs_FileID *)ioctlPtr->inBuffer, &openResults);
if (ioctlPtr->flags & FS_USER_OUT) {
Vm_CopyOut(sizeof(int), (Address)&newStreamID,
ioctlPtr->outBuffer);
} else {
*(int *)ioctlPtr->outBuffer = newStreamID;
}
if (newStreamID < 0) {
status = FAILURE;
} else {
/*
* Here we copy the openResults to the waiting processes
* kernel stack (it's waiting in FspdevPfsOpen on this
* same host.)
*/
register Fs_OpenResults *openResultsPtr =
(Fs_OpenResults *)pdevHandlePtr->replyBuf;
*openResultsPtr = openResults;
}
}
if (status != SUCCESS) {
pdevHandlePtr->flags |= PDEV_REPLY_FAILED;
pdevHandlePtr->reply.replySize = 0;
} else {
pdevHandlePtr->reply.replySize = sizeof(Fs_OpenResults);
}
pdevHandlePtr->reply.status = status;
pdevHandlePtr->flags |= PDEV_REPLY_READY;
Sync_Broadcast(&pdevHandlePtr->replyReady);
PdevClientNotify(pdevHandlePtr);
break;
}
case IOC_PFS_SET_ID: {
/*
* A pseudo-filesystem server is setting its own notion of the
* fileID associated with a request-response stream.
*/
register Fs_FileID *fileIDPtr;
if (ioctlPtr->inBufSize < sizeof(Fs_FileID)) {
status = FS_INVALID_ARG;
} else {
fileIDPtr = (Fs_FileID *)ioctlPtr->inBuffer;
pdevHandlePtr->userLevelID = *fileIDPtr;
}
break;
}
case IOC_PFS_PASS_STREAM: {
/*
* A pseudo-filesystem server is replying to an open request by
* asking us to pass one of its open streams to the client.
*/
register int passedStreamID; /* From the server */
Fs_OpenResults openResults; /* For the client */
Address encapStream; /* packaged stream */
int encapSize; /* size of package */
register Proc_ControlBlock *procPtr;
register Fs_OpenResults *openResultsPtr;
if (ioctlPtr->inBufSize < sizeof(int)) {
status = GEN_INVALID_ARG;
} else if (pdevHandlePtr->flags & PDEV_REQUEST_ABORTED) {
pdevHandlePtr->flags &= ~PDEV_REQUEST_ABORTED;
PdevClientNotify(pdevHandlePtr);
break;
} else {
passedStreamID = *(int *)ioctlPtr->inBuffer;
procPtr = Proc_GetEffectiveProc();
status = Fs_GetStreamPtr(procPtr, passedStreamID, &streamPtr);
if (status == SUCCESS) {
encapSize = Fs_GetEncapSize();
encapStream = (Address)malloc(encapSize);
status = Fsio_EncapStream(streamPtr, encapStream);
if (status == SUCCESS) {
/*
* Set up open results so Fs_Open will call a cltOpen
* routine that will unpackage the encapsulated stream.
*/
bzero((char *)&openResults, sizeof(Fs_OpenResults));
openResults.ioFileID.type = FSIO_PASSING_STREAM;
openResults.dataSize = encapSize;
openResults.streamData = (ClientData)encapStream;
openResultsPtr =
(Fs_OpenResults *)pdevHandlePtr->replyBuf;
*openResultsPtr = openResults;
}
}
}
if (status != SUCCESS) {
pdevHandlePtr->flags |= PDEV_REPLY_FAILED;
pdevHandlePtr->reply.replySize = 0;
} else {
pdevHandlePtr->reply.replySize = sizeof(Fs_OpenResults);
}
pdevHandlePtr->reply.status = status;
pdevHandlePtr->flags |= PDEV_REPLY_READY;
Sync_Broadcast(&pdevHandlePtr->replyReady);
PdevClientNotify(pdevHandlePtr);
break;
}
case IOC_REPOSITION:
status = GEN_INVALID_ARG;
break;
case IOC_GET_FLAGS:
case IOC_SET_FLAGS:
case IOC_SET_BITS:
case IOC_CLEAR_BITS:
/*
* There are no server stream specific flags.
*/
break;
case IOC_TRUNCATE:
case IOC_LOCK:
case IOC_UNLOCK:
case IOC_MAP:
status = FS_INVALID_ARG;
break;
case IOC_GET_OWNER:
case IOC_SET_OWNER:
status = GEN_NOT_IMPLEMENTED;
break;
case IOC_NUM_READABLE: {
/*
* The server stream is readable only if there are requests in the
* request buffer or if the read ahead buffers have changed since
* the last time the server did a read.
*
* Side effects:
* None.
*/
register int reqFirstByte, reqLastByte;
register int numReadable;
reqFirstByte = pdevHandlePtr->requestBuf.firstByte;
reqLastByte = pdevHandlePtr->requestBuf.lastByte;
if (((pdevHandlePtr->flags & PDEV_READ_PTRS_CHANGED) == 0) &&
((reqFirstByte == -1) || (reqFirstByte > reqLastByte))) {
numReadable = 0;
} else {
numReadable = sizeof(Pdev_BufPtrs);
}
if (ioctlPtr->outBuffer == (Address)NIL ||
ioctlPtr->outBufSize < sizeof(int)) {
status = GEN_INVALID_ARG;
} else {
*(int *)ioctlPtr->outBuffer = numReadable;
status = SUCCESS;
}
break;
}
case IOC_PREFIX:
status = SUCCESS;
break;
default:
status = GEN_NOT_IMPLEMENTED;
break;
}
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* FspdevSignalOwner --
*
* Send a signal to the owning process or family of a pseudo-device.
*
* Results:
* FS_INVALID_ARG if the input buffer isn't right.
*
* Side effects:
* Bypasses permissions and sends the signal. Permissions have to
* be bypassed otherwise the pseudo-device server has to be setuid.
*
*----------------------------------------------------------------------
*/
ReturnStatus
FspdevSignalOwner(ctrlHandlePtr, ioctlPtr)
Fspdev_ControlIOHandle *ctrlHandlePtr;
Fs_IOCParam *ioctlPtr;
{
register ReturnStatus status;
register Pdev_Signal *sigPtr;
register Ioc_Owner *ownerPtr = &ctrlHandlePtr->owner;
register Proc_ControlBlock *procPtr;
if (ownerPtr->procOrFamily == 0) {
/*
* No owner declared, this is a no-op.
*/
status = SUCCESS;
} else if (ioctlPtr->inBufSize != sizeof(Pdev_Signal)) {
status = FS_INVALID_ARG;
} else {
register int savedEuid;
sigPtr = (Pdev_Signal *)ioctlPtr->inBuffer;
procPtr = Proc_GetEffectiveProc();
savedEuid = procPtr->effectiveUserID;
procPtr->effectiveUserID = 0;
status = Sig_Send(sigPtr->signal, sigPtr->code, ownerPtr->id,
(ownerPtr->procOrFamily == IOC_OWNER_FAMILY), (Address)0);
procPtr->effectiveUserID = savedEuid;
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* FspdevServerStreamClose --
*
* Clean up the state associated with a server stream. This makes
* sure the client processes associated with the pseudo stream get
* poked, and it marks the pseudo stream's state as invalid so
* the clients will abort their current operations, if any. The
* handle is 'removed' here, but it won't go away until the client
* side closes down and releases its reference to it.
*
* Results:
* SUCCESS.
*
* Side effects:
* Marks the pseudo stream state with PDEV_SERVER_GONE, notifies
* all conditions in pseudo stream state, wakes up all processes
* in any of the pseudo stream's wait lists, and then removes
* the handle from the hash table.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
#ifdef SOSP91
ReturnStatus
FspdevServerStreamClose(streamPtr, clientID, procID, flags, size, data,
offsetPtr, rwFlagsPtr)
#else
ReturnStatus
FspdevServerStreamClose(streamPtr, clientID, procID, flags, size, data)
#endif
Fs_Stream *streamPtr; /* Service stream to close */
int clientID; /* HostID of client closing */
Proc_PID procID; /* ID of closing process */
int flags; /* Flags from the stream being closed */
int size; /* Should be zero */
ClientData data; /* IGNORED */
#ifdef SOSP91
int *offsetPtr;
int *rwFlagsPtr;
#endif
{
register Fspdev_ServerIOHandle *pdevHandlePtr =
(Fspdev_ServerIOHandle *)streamPtr->ioHandlePtr;
DBG_PRINT( ("Server Closing pdev %x,%x\n",
pdevHandlePtr->hdr.fileID.major,
pdevHandlePtr->hdr.fileID.minor) );
PdevClientWakeup(pdevHandlePtr);
if (pdevHandlePtr->flags & PDEV_NAMING) {
/*
* Clean up the prefix table entry associated with the pfs.
*/
register Fspdev_ControlIOHandle *ctrlHandlePtr;
Fs_Stream dummy;
ctrlHandlePtr = pdevHandlePtr->ctrlHandlePtr;
dummy.hdr.fileID.type = -1;
dummy.ioHandlePtr = (Fs_HandleHeader *)ctrlHandlePtr;
Fsutil_HandleLock(ctrlHandlePtr);
Fsprefix_HandleClose(ctrlHandlePtr->prefixPtr, FSPREFIX_ANY);
#ifdef SOSP91
(void)FspdevControlClose(&dummy, clientID, procID, flags, 0,
(ClientData)NIL, (int *) NIL, (int *) NIL);
#else
(void)FspdevControlClose(&dummy, clientID, procID, flags, 0, (ClientData)NIL);
#endif
}
Sync_LockClear(&pdevHandlePtr->lock);
Fsutil_HandleRelease(pdevHandlePtr, TRUE);
Fsutil_HandleRemove(pdevHandlePtr); /* No need for scavenging */
fs_Stats.object.pseudoStreams--;
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* PdevClientWakeup --
*
* Called when the server's stream is closed. This
* notifies the various conditions that the client might be
* waiting on and marks the pdev state as invalid so the
* client will bail out when it wakes up.
*
* Results:
* None.
*
* Side effects:
* Notifies condition variables and marks the pseudo stream as invalid.
*
*----------------------------------------------------------------------
*/
ENTRY static void
PdevClientWakeup(pdevHandlePtr)
Fspdev_ServerIOHandle *pdevHandlePtr; /* State for the pseudo stream */
{
LOCK_MONITOR;
/*
* Set both "busy" and "server gone" because the standard preamble
* for all client routines only checks against "server gone"
* inside a while-not-busy loop.
*/
pdevHandlePtr->flags |= (PDEV_SERVER_GONE|PDEV_REPLY_FAILED|PDEV_BUSY);
Sync_Broadcast(&pdevHandlePtr->access);
Sync_Broadcast(&pdevHandlePtr->caughtUp);
Sync_Broadcast(&pdevHandlePtr->replyReady);
Fsutil_FastWaitListNotify(&pdevHandlePtr->cltReadWaitList);
Fsutil_FastWaitListNotify(&pdevHandlePtr->cltWriteWaitList);
Fsutil_FastWaitListNotify(&pdevHandlePtr->cltExceptWaitList);
Fsutil_WaitListDelete(&pdevHandlePtr->cltReadWaitList);
Fsutil_WaitListDelete(&pdevHandlePtr->cltWriteWaitList);
Fsutil_WaitListDelete(&pdevHandlePtr->cltExceptWaitList);
Fsutil_WaitListDelete(&pdevHandlePtr->srvReadWaitList);
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* FspdevPdevServerOK --
*
* Called from FspdevPfsExport to see if the server of a prefix still exists.
*
* Results:
* TRUE if the server process is still around.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ENTRY Boolean
FspdevPdevServerOK(pdevHandlePtr)
Fspdev_ServerIOHandle *pdevHandlePtr; /* State for the pseudo stream */
{
register Boolean answer;
LOCK_MONITOR;
if (pdevHandlePtr->flags & PDEV_SERVER_GONE) {
answer = FALSE;
} else {
answer = TRUE;
}
UNLOCK_MONITOR;
return(answer);
}
/*
*----------------------------------------------------------------------
*
* FspdevPseudoStreamOpen --
*
* Do the first request-response with a pseudo-device server to see if it
* will accept the open by the client. This is used to tell
* the server something about the new stream it is getting,
* and to let it decide if it will accept the open.
*
* Results:
* The return status from the server's reply. It can reject the open.
*
* Side effects:
* An PDEV_OPEN request-response is carried out.
*
*----------------------------------------------------------------------
*/
ENTRY ReturnStatus
FspdevPseudoStreamOpen(pdevHandlePtr, flags, clientID, procID, userID)
register Fspdev_ServerIOHandle *pdevHandlePtr; /* Pdev connection state */
int flags; /* Open flags */
int clientID; /* Host ID of the client */
Proc_PID procID; /* Process ID of the client process */
int userID; /* User ID of the client process */
{
register ReturnStatus status;
Pdev_Request request;
LOCK_MONITOR;
/*
* Wait for the server to set up the request buffer. The state starts
* out PDEV_BUSY to eliminate an explicit check for PDEV_SETUP in all
* the routines that call RequestResponse. PDEV_BUSY is cleared after
* the server initializes the request buffer with IOC_PDEV_SETUP.
*/
while (pdevHandlePtr->flags & PDEV_BUSY) {
if ((pdevHandlePtr->flags & PDEV_SERVER_GONE) == 0) {
(void)Sync_Wait(&pdevHandlePtr->access, FALSE);
}
if (pdevHandlePtr->flags & PDEV_SERVER_GONE) {
status = DEV_OFFLINE;
goto exit;
}
}
pdevHandlePtr->flags |= PDEV_BUSY;
/*
* Issue the first request to the server to see if it will accept us.
*/
request.hdr.operation = PDEV_OPEN;
request.param.open.flags = flags;
request.param.open.pid = procID;
request.param.open.hostID = clientID;
request.param.open.uid = userID;
pdevHandlePtr->flags &= ~(FS_USER_IN|FS_USER_OUT);
status = RequestResponse(pdevHandlePtr, sizeof(Pdev_Request), &request.hdr,
0, (Address) NIL, 0, (Address) NIL, (Fs_IOReply *)NIL,
(Sync_RemoteWaiter *)NIL);
pdevHandlePtr->flags &= ~PDEV_BUSY;
exit:
Sync_Broadcast(&pdevHandlePtr->access);
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* FspdevPseudoStreamLookup --
*
* Do a lookup request-response to a pseudo-filesystem server over
* the naming request-response stream that is hooked to the prefix table.
* If a pathname redirection occurs this allocates a buffer for the
* returned pathname. Otherwise this is a stub that passes its arguments
* up to the user level pseudo-filesystem server via request-response.
*
* Results:
* A status and some bundled results that aren't interpreted by us.
* If status is FS_REDIRECT then *newNameInfoPtrPtr has been allocated
* and contains the returned pathname.
*
* Side effects:
* The effect of the naming operation in the pseudo-filesystem is
* up to the pseudo-filesystem server. Upon FS_REDIRECT this
* allocates the *newNameInfoPtrPtr buffer.
*
*----------------------------------------------------------------------
*/
ENTRY ReturnStatus
FspdevPseudoStreamLookup(pdevHandlePtr, requestPtr, argSize, argsPtr,
resultsSizePtr, resultsPtr, newNameInfoPtrPtr)
register Fspdev_ServerIOHandle *pdevHandlePtr; /* Pdev connection state */
Pfs_Request *requestPtr; /* Semi-initialized request header */
int argSize; /* Size of argsPtr buffer */
Address argsPtr; /* Ref. to bundled args, usually name */
int *resultsSizePtr;/* Out size of results */
Address resultsPtr; /* Ref. to bundled results or
* Fs_RedirectInfo */
Fs_RedirectInfo **newNameInfoPtrPtr;/* Set if server returns name */
{
register ReturnStatus status;
Fs_RedirectInfo redirectInfo; /* This is a large buffer
* that is used for EITHER
* normal reply parameters
* or a redirected pathname */
Fs_IOReply ioReply;
LOCK_MONITOR;
*newNameInfoPtrPtr = (Fs_RedirectInfo *)NIL;
while (pdevHandlePtr->flags & PDEV_BUSY) {
if ((pdevHandlePtr->flags & PDEV_SERVER_GONE) == 0) {
(void)Sync_Wait(&pdevHandlePtr->access, FALSE);
}
if (pdevHandlePtr->flags & PDEV_SERVER_GONE) {
status = FS_STALE_HANDLE;
goto exit;
}
}
pdevHandlePtr->flags |= PDEV_BUSY;
pdevHandlePtr->flags &= ~(FS_USER_IN|FS_USER_OUT);
status = RequestResponse(pdevHandlePtr, sizeof(Pfs_Request),
&requestPtr->hdr, argSize, argsPtr, sizeof(Fs_RedirectInfo),
(Address)&redirectInfo, &ioReply,
(Sync_RemoteWaiter *)NIL);
if (status == FS_LOOKUP_REDIRECT) {
*newNameInfoPtrPtr = mnew(Fs_RedirectInfo);
(*newNameInfoPtrPtr)->prefixLength = redirectInfo.prefixLength;
(void)strcpy((*newNameInfoPtrPtr)->fileName, redirectInfo.fileName);
} else {
/*
* Grab the result parameters.
*/
*resultsSizePtr = ioReply.length;
if (ioReply.length > 0) {
bcopy((Address)&redirectInfo, resultsPtr, ioReply.length);
}
}
if (status == DEV_OFFLINE) {
/*
* Return stale handle so remote clients know to nuke
* their prefix table entry.
*/
status = FS_STALE_HANDLE;
}
pdevHandlePtr->flags &= ~PDEV_BUSY;
exit:
Sync_Broadcast(&pdevHandlePtr->access);
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* FspdevPseudoStream2Path --
*
* Do a rename/link request-response to a pseudo-filesystem server over
* the naming request-response stream that is hooked to the prefix table.
* If a pathname redirection occurs this allocates a buffer for the
* returned pathname. Otherwise this is a stub that passes its arguments
* up to the user level pseudo-filesystem server via request-response.
*
* Results:
* A status and some bundled results that aren't interpreted by us.
* If status is FS_REDIRECT then *newNameInfoPtrPtr has been allocated
* and contains the returned pathname.
*
* Side effects:
* The effect of the naming operation in the pseudo-filesystem is
* up to the pseudo-filesystem server. Upon FS_REDIRECT this
* allocates the *newNameInfoPtrPtr buffer.
*
*----------------------------------------------------------------------
*/
ENTRY ReturnStatus
FspdevPseudoStream2Path(pdevHandlePtr, requestPtr, dataPtr, name1ErrorPtr,
newNameInfoPtrPtr)
register Fspdev_ServerIOHandle *pdevHandlePtr; /* Pdev connection state */
Pfs_Request *requestPtr; /* Semi-initialized request header */
Fs_2PathData *dataPtr; /* 2 pathnames */
Boolean *name1ErrorPtr; /* TRUE if error applies to first path*/
Fs_RedirectInfo **newNameInfoPtrPtr;/* Set if server returns name */
{
register ReturnStatus status;
Fs_2PathRedirectInfo redirectInfo;
LOCK_MONITOR;
while (pdevHandlePtr->flags & PDEV_BUSY) {
if ((pdevHandlePtr->flags & PDEV_SERVER_GONE) == 0) {
(void)Sync_Wait(&pdevHandlePtr->access, FALSE);
}
if (pdevHandlePtr->flags & PDEV_SERVER_GONE) {
status = FS_STALE_HANDLE;
goto exit;
}
}
pdevHandlePtr->flags |= PDEV_BUSY;
pdevHandlePtr->flags &= ~(FS_USER_IN|FS_USER_OUT);
status = RequestResponse(pdevHandlePtr, sizeof(Pfs_Request),
&requestPtr->hdr, sizeof(Fs_2PathData), (Address)dataPtr,
sizeof(Fs_2PathRedirectInfo), (Address)&redirectInfo,
(Fs_IOReply *)NIL, (Sync_RemoteWaiter *)NIL);
if (status == FS_LOOKUP_REDIRECT) {
*newNameInfoPtrPtr = mnew(Fs_RedirectInfo);
(*newNameInfoPtrPtr)->prefixLength = redirectInfo.prefixLength;
(void)strcpy((*newNameInfoPtrPtr)->fileName, redirectInfo.fileName);
}
if (status != SUCCESS) {
*name1ErrorPtr = redirectInfo.name1ErrorP;
} else {
*name1ErrorPtr = FALSE;
}
if (status == DEV_OFFLINE) {
/*
* Return stale handle so remote clients know to nuke
* their prefix table entry.
*/
status = FS_STALE_HANDLE;
}
pdevHandlePtr->flags &= ~PDEV_BUSY;
exit:
Sync_Broadcast(&pdevHandlePtr->access);
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* FspdevPseudoGetAttr --
*
* Called from Fs_GetAttributesID to get the attributes of a file
* in a pseudo-filesystem. The stream's nameInfoPtr->fileID is
* set to the fileID of the pseudo-device connection to the server,
* and this ID is passed into us. This does a PDEV_GET_ATTR request.
*
* Results:
* The attributes structure is passed back from the pfs server.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
FspdevPseudoGetAttr(fileIDPtr, clientID, attrPtr)
register Fs_FileID *fileIDPtr; /* Identfies pdev connection */
int clientID; /* Host ID of process asking
* for the attributes */
register Fs_Attributes *attrPtr; /* Return - the attributes */
{
Fspdev_ClientIOHandle *cltHandlePtr;
Pdev_Request request;
register Fspdev_ServerIOHandle *pdevHandlePtr;
register ReturnStatus status;
cltHandlePtr = Fsutil_HandleFetchType(Fspdev_ClientIOHandle, fileIDPtr);
if (cltHandlePtr == (Fspdev_ClientIOHandle *)NIL) {
printf( "FspdevPseudoGetAttr, no %s handle <%d,%x,%x>\n",
Fsutil_FileTypeToString(fileIDPtr->type), fileIDPtr->serverID,
fileIDPtr->major, fileIDPtr->minor);
return(FS_FILE_NOT_FOUND);
}
Fsutil_HandleRelease(cltHandlePtr, TRUE);
pdevHandlePtr = cltHandlePtr->pdevHandlePtr;
LOCK_MONITOR;
while (pdevHandlePtr->flags & PDEV_BUSY) {
if ((pdevHandlePtr->flags & PDEV_SERVER_GONE) == 0) {
(void)Sync_Wait(&pdevHandlePtr->access, FALSE);
}
if (pdevHandlePtr->flags & PDEV_SERVER_GONE) {
status = DEV_OFFLINE;
goto exit;
}
}
pdevHandlePtr->flags |= PDEV_BUSY;
pdevHandlePtr->flags &= ~(FS_USER_IN|FS_USER_OUT);
request.hdr.operation = PDEV_GET_ATTR;
status = RequestResponse(pdevHandlePtr, sizeof(Pdev_Request), &request.hdr,
0, (Address) NIL,
sizeof(Fs_Attributes), (Address)attrPtr,
(Fs_IOReply *)NIL, (Sync_RemoteWaiter *)NIL);
/*
* Patch the serverID in the attributes so it matches the serverID
* given in the prefix table. This is needed to make getwd() work.
*/
attrPtr->serverID = rpc_SpriteID;
pdevHandlePtr->flags &= ~PDEV_BUSY;
exit:
Sync_Broadcast(&pdevHandlePtr->access);
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* FspdevPseudoSetAttr --
*
* Set the attributes of a file in a pseudo-filesystem. We have the
* fileID of the request-response stream to the server. This issues
* a PDEV_SET_ATTR request to the server that contains new attributes.
*
* Results:
* An error code.
*
* Side effects:
* None here. The new attributes are shipped to the pfs server.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
FspdevPseudoSetAttr(fileIDPtr, attrPtr, idPtr, flags)
register Fs_FileID *fileIDPtr; /* Identfies pdev connection */
register Fs_Attributes *attrPtr; /* Return - the attributes */
Fs_UserIDs *idPtr; /* Identfies user */
int flags; /* Tells which attrs to set */
{
Fspdev_ClientIOHandle *cltHandlePtr;
Pdev_Request request;
register Fspdev_ServerIOHandle *pdevHandlePtr;
register ReturnStatus status;
cltHandlePtr = Fsutil_HandleFetchType(Fspdev_ClientIOHandle, fileIDPtr);
if (cltHandlePtr == (Fspdev_ClientIOHandle *)NIL) {
printf( "FspdevPseudoSetAttr, no handle <%d,%d,%x,%x>\n",
fileIDPtr->serverID, fileIDPtr->type,
fileIDPtr->major, fileIDPtr->minor);
return(FS_FILE_NOT_FOUND);
}
Fsutil_HandleRelease(cltHandlePtr, TRUE);
pdevHandlePtr = cltHandlePtr->pdevHandlePtr;
LOCK_MONITOR;
while (pdevHandlePtr->flags & PDEV_BUSY) {
if ((pdevHandlePtr->flags & PDEV_SERVER_GONE) == 0) {
(void)Sync_Wait(&pdevHandlePtr->access, FALSE);
}
if (pdevHandlePtr->flags & PDEV_SERVER_GONE) {
status = DEV_OFFLINE;
goto exit;
}
}
pdevHandlePtr->flags |= PDEV_BUSY;
pdevHandlePtr->flags &= ~(FS_USER_IN|FS_USER_OUT);
request.hdr.operation = PDEV_SET_ATTR;
request.param.setAttr.uid = idPtr->user;
request.param.setAttr.flags = flags;
status = RequestResponse(pdevHandlePtr, sizeof(Pdev_Request), &request.hdr,
sizeof(Fs_Attributes), (Address)attrPtr,
0, (Address) NIL,
(Fs_IOReply *)NIL, (Sync_RemoteWaiter *)NIL);
pdevHandlePtr->flags &= ~PDEV_BUSY;
exit:
Sync_Broadcast(&pdevHandlePtr->access);
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* FspdevPseudoStreamGetIOAttr --
*
* Called from Fs_GetAttrStream to get the I/O attributes of a
* pseudo-device. The access and modify times of the pseudo-device
* are obtained from the internal pdev state.
*
* Results:
* SUCCESS.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ReturnStatus
FspdevPseudoStreamGetIOAttr(fileIDPtr, clientID, attrPtr)
register Fs_FileID *fileIDPtr; /* Identfies pdev connection */
int clientID; /* Host ID of process asking
* for the attributes */
register Fs_Attributes *attrPtr; /* Return - the attributes */
{
Fspdev_ClientIOHandle *cltHandlePtr;
register Fspdev_ServerIOHandle *pdevHandlePtr;
cltHandlePtr = Fsutil_HandleFetchType(Fspdev_ClientIOHandle, fileIDPtr);
if (cltHandlePtr == (Fspdev_ClientIOHandle *)NIL) {
printf( "FspdevPseudoStreamGetIOAttr, no %s handle <%d,%x,%x> client %d\n",
Fsutil_FileTypeToString(fileIDPtr->type), fileIDPtr->serverID,
fileIDPtr->major, fileIDPtr->minor, clientID);
return(FS_FILE_NOT_FOUND);
}
Fsutil_HandleRelease(cltHandlePtr, TRUE);
pdevHandlePtr = cltHandlePtr->pdevHandlePtr;
LOCK_MONITOR;
attrPtr->accessTime.seconds = pdevHandlePtr->ctrlHandlePtr->accessTime;
attrPtr->dataModifyTime.seconds = pdevHandlePtr->ctrlHandlePtr->modifyTime;
UNLOCK_MONITOR;
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* FspdevPseudoStreamSetIOAttr --
*
* Set the IO attributes of a pseudo-device.
*
* Results:
* An error code.
*
* Side effects:
* Updates the access and modify times kept in the pdev state.
*
*----------------------------------------------------------------------
*/
ReturnStatus
FspdevPseudoStreamSetIOAttr(fileIDPtr, attrPtr, flags)
register Fs_FileID *fileIDPtr; /* Identfies pdev connection */
register Fs_Attributes *attrPtr; /* Return - the attributes */
int flags; /* Tells which attrs to set */
{
Fspdev_ClientIOHandle *cltHandlePtr;
register Fspdev_ServerIOHandle *pdevHandlePtr;
cltHandlePtr = Fsutil_HandleFetchType(Fspdev_ClientIOHandle, fileIDPtr);
if (cltHandlePtr == (Fspdev_ClientIOHandle *)NIL) {
printf( "FspdevPseudoStreamSetIOAttr, no handle <%d,%d,%x,%x>\n",
fileIDPtr->serverID, fileIDPtr->type,
fileIDPtr->major, fileIDPtr->minor);
return(FS_FILE_NOT_FOUND);
}
Fsutil_HandleRelease(cltHandlePtr, TRUE);
pdevHandlePtr = cltHandlePtr->pdevHandlePtr;
LOCK_MONITOR;
if (flags & FS_SET_TIMES) {
pdevHandlePtr->ctrlHandlePtr->accessTime = attrPtr->accessTime.seconds;
pdevHandlePtr->ctrlHandlePtr->modifyTime = attrPtr->dataModifyTime.seconds;
}
UNLOCK_MONITOR;
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* FspdevPseudoStreamRead --
*
* Read from a pseudo-device stream. If there is data in the read
* ahead buffer (if one exists), that is used to satisfy the read.
* Otherwise a request-response exchange with the server is used
* to do the read.
*
* Results:
* SUCCESS, and *lenPtr reflects how much was read. When the server
* goes away EOF is simulated by a SUCCESS return and *lenPtr == 0.
* If there is no data in the read ahead buffer FS_WOULD_BLOCK is returned.
*
* Side effects:
* If applicable, pointers into the read ahead buffer are adjusted.
* The buffer is filled with the number of bytes indicated by
* the length parameter. The in/out length parameter specifies
* the buffer size on input and is updated to reflect the number
* of bytes actually read.
*
*----------------------------------------------------------------------
*/
ReturnStatus
FspdevPseudoStreamRead(streamPtr, readPtr, waitPtr, replyPtr)
register Fs_Stream *streamPtr; /* Stream to read from. */
Fs_IOParam *readPtr; /* Read parameter block. */
Sync_RemoteWaiter *waitPtr; /* Process info for remote waiting */
Fs_IOReply *replyPtr; /* Signal to return, if any,
* plus the amount read. */
{
ReturnStatus status;
register Fspdev_ClientIOHandle *cltHandlePtr =
(Fspdev_ClientIOHandle *)streamPtr->ioHandlePtr;
register Fspdev_ServerIOHandle *pdevHandlePtr = cltHandlePtr->pdevHandlePtr;
Pdev_Request request;
LOCK_MONITOR;
while (pdevHandlePtr->flags & PDEV_BUSY) {
if ((pdevHandlePtr->flags & PDEV_SERVER_GONE) == 0) {
(void)Sync_Wait(&pdevHandlePtr->access, FALSE);
}
if (pdevHandlePtr->flags & PDEV_SERVER_GONE) {
status = DEV_OFFLINE;
goto exitNoServer;
}
}
pdevHandlePtr->flags |= PDEV_BUSY;
#ifdef notdef
{
register Ioc_Owner *ownerPtr;
/*
* Check for access inside the owning process group.
*/
ownerPtr = &pdevHandlePtr->ctrlHandlePtr->owner;
if (((ownerPtr->procOrFamily == IOC_OWNER_FAMILY) &&
(readPtr->familyID != ownerPtr->id)) ||
((ownerPtr->procOrFamily == IOC_OWNER_PROC) &&
(readPtr->procID != ownerPtr->id))) {
printf("PdevRead: ownership conflict\n");
status = GEN_ABORTED_BY_SIGNAL;
replyPtr->signal = SIG_TTY_INPUT;
goto exit;
}
}
#endif
if (pdevHandlePtr->readBuf.data != (Address)NIL) {
/*
* A read ahead buffer exists so we get data from it. If it's
* empty we put the client on the client I/O handle read wait list.
*/
if (pdevHandlePtr->flags & PDEV_READ_BUF_EMPTY) {
status = FS_WOULD_BLOCK;
Fsutil_FastWaitListInsert(&pdevHandlePtr->cltReadWaitList, waitPtr);
PDEV_TRACE(&pdevHandlePtr->hdr.fileID, PDEVT_READ_WAIT);
DBG_PRINT( ("PDEV %x,%x Read (%d) Blocked\n",
streamPtr->ioHandlePtr->fileID.major,
streamPtr->ioHandlePtr->fileID.minor,
readPtr->length) );
replyPtr->length = 0;
} else {
register int dataAvail, firstByte, lastByte, toRead;
register Proc_ControlBlock *serverProcPtr;
firstByte = pdevHandlePtr->readBuf.firstByte;
lastByte = pdevHandlePtr->readBuf.lastByte;
dataAvail = lastByte - firstByte + 1;
if (dataAvail <= 0) {
panic("FspdevPseudoStreamRead, dataAvail in read buf <= 0 bytes\n");
status = DEV_OFFLINE;
goto exit;
}
/*
* Lock down the server process in preparation for copying
* from the read ahead buffer.
*/
serverProcPtr = Proc_LockPID(pdevHandlePtr->serverPID);
if (serverProcPtr == (Proc_ControlBlock *)NIL) {
status = DEV_OFFLINE;
goto exit;
}
/*
* Decide how much to read and note if we empty the buffer.
*/
if (dataAvail > readPtr->length) {
toRead = readPtr->length;
} else {
toRead = dataAvail;
pdevHandlePtr->flags |= PDEV_READ_BUF_EMPTY;
}
DBG_PRINT( ("PDEV %x,%x Read %d Avail %d\n",
pdevHandlePtr->hdr.fileID.major,
pdevHandlePtr->hdr.fileID.minor,
toRead, dataAvail) );
/*
* Copy out of the read ahead buffer to the client's buffer.
*/
status = Vm_CopyInProc(toRead, serverProcPtr,
pdevHandlePtr->readBuf.data + firstByte,
readPtr->buffer, (readPtr->flags & FS_USER) == 0);
Proc_Unlock(serverProcPtr);
/*
* Update pointers and poke the server so it can find out.
*/
replyPtr->length = toRead;
pdevHandlePtr->readBuf.firstByte = firstByte + toRead;
pdevHandlePtr->flags |= PDEV_READ_PTRS_CHANGED;
Fsutil_FastWaitListNotify(&pdevHandlePtr->srvReadWaitList);
}
} else if (pdevHandlePtr->selectBits & FS_READABLE) {
/*
* No read ahead buffer and the state is readable.
* Set up and do the request-response exchange.
*/
request.hdr.operation = PDEV_READ;
request.param.read = *readPtr;
if (readPtr->flags & FS_USER) {
pdevHandlePtr->flags |= FS_USER_OUT;
}
status = RequestResponse(pdevHandlePtr, sizeof(Pdev_Request),
&request.hdr, 0, (Address) NIL, readPtr->length, readPtr->buffer,
replyPtr, waitPtr);
} else {
/*
* The pseudo-device is not readable now.
*/
Fsutil_FastWaitListInsert(&pdevHandlePtr->cltReadWaitList, waitPtr);
replyPtr->length = 0;
status = FS_WOULD_BLOCK;
}
if (status == SUCCESS) {
pdevHandlePtr->ctrlHandlePtr->accessTime = Fsutil_TimeInSeconds();
}
exit:
if (status == DEV_OFFLINE) {
/*
* Simulate EOF
*/
status = SUCCESS;
replyPtr->length = 0;
}
pdevHandlePtr->flags &= ~(PDEV_BUSY|FS_USER_OUT);
exitNoServer:
Sync_Broadcast(&pdevHandlePtr->access);
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* FspdevPseudoStreamWrite --
*
* Write to a pseudo-device by a client. The write is done
* asynchronously if the stream allows that. In that case we
* always say we could write as much as requested. However, if
* the write is larger than the server's request buffer we
* break it into blocks that each fit. To support UDP, the
* stream can also be marked to dis-allow these large writes.
* Finally, the stream may be marked synchronous in which case
* we tell RequestResponse to wait for a reply.
*
* Results:
* SUCCESS - the data was written.
*
* Side effects:
* The data in the buffer is written to the device. Large writes
* are broken into a series of shorter writes, although we keep
* the access lock on the pseudo-stream so the whole write completes.
* The in/out length parameter specifies the amount of data to write
* and is updated to reflect the number of bytes actually written.
*
*----------------------------------------------------------------------
*/
ENTRY ReturnStatus
FspdevPseudoStreamWrite(streamPtr, writePtr, waitPtr, replyPtr)
Fs_Stream *streamPtr; /* Stream to write to. */
Fs_IOParam *writePtr; /* Read parameter block */
Sync_RemoteWaiter *waitPtr; /* Process info for remote waiting */
Fs_IOReply *replyPtr; /* Signal to return, if any */
{
register Fspdev_ClientIOHandle *cltHandlePtr =
(Fspdev_ClientIOHandle *)streamPtr->ioHandlePtr;
register Fspdev_ServerIOHandle *pdevHandlePtr = cltHandlePtr->pdevHandlePtr;
ReturnStatus status = SUCCESS;
Pdev_Request request;
int amountWritten;
int numBytes;
int maxRequestSize;
int totalToWrite;
LOCK_MONITOR;
/*
* Wait for exclusive access to the stream.
*/
while (pdevHandlePtr->flags & PDEV_BUSY) {
if ((pdevHandlePtr->flags & PDEV_SERVER_GONE) == 0) {
(void)Sync_Wait(&pdevHandlePtr->access, FALSE);
}
if (pdevHandlePtr->flags & PDEV_SERVER_GONE) {
status = FS_BROKEN_PIPE;
goto exitNoServer;
}
}
pdevHandlePtr->flags |= PDEV_BUSY;
if (writePtr->flags & FS_USER) {
pdevHandlePtr->flags |= FS_USER_IN;
}
#ifdef notdef
{
/*
* Check for access inside the owning process group.
* (This doesn't work presently, 6/89. We'll rely on
* the tty driver to do this checking for us.)
*
* Getting 4.3 BSD semantics is tricky because the operation is
* supposed to succeed if the TTYOUT signal is blocked. Need a
* flag passed down from Fs_Write.
*/
Ioc_Owner *ownerPtr;
ownerPtr = &pdevHandlePtr->ctrlHandlePtr->owner;
if (((ownerPtr->procOrFamily == IOC_OWNER_FAMILY) &&
(writePtr->familyID != ownerPtr->id)) ||
((ownerPtr->procOrFamily == IOC_OWNER_PROC) &&
(writePtr->procID != ownerPtr->id))) {
printf("PdevWrite: ownership conflict\n");
status = GEN_ABORTED_BY_SIGNAL;
replyPtr->signal = SIG_TTY_OUTPUT;
goto exit;
}
}
#endif
/*
* Allow flow control by checking the select bits and only trying
* the operation if the state allows it.
*/
if ((pdevHandlePtr->selectBits & FS_WRITABLE) == 0) {
replyPtr->length = 0;
Fsutil_FastWaitListInsert(&pdevHandlePtr->cltWriteWaitList, waitPtr);
status = FS_WOULD_BLOCK;
goto exit;
}
maxRequestSize = pdevHandlePtr->requestBuf.size - sizeof(Pdev_Request);
if (writePtr->length > maxRequestSize &&
(pdevHandlePtr->flags & PDEV_NO_BIG_WRITES)) {
printf("Too large a write (%d bytes) an a (UDP?) pseudo-device\n",
writePtr->length);
status = GEN_INVALID_ARG;
goto exit;
}
if (pdevHandlePtr->flags & PDEV_WRITE_BEHIND) {
request.hdr.operation = PDEV_WRITE_ASYNC;
} else {
request.hdr.operation = PDEV_WRITE;
}
request.param.write = *writePtr;
request.param.write.buffer = 0;
amountWritten = 0;
totalToWrite = writePtr->length;
while ((writePtr->length > 0) && (status == SUCCESS)) {
/*
* Loop to put the maximum amount of data into the request
* buffer until the whole block has been transferred. The
* server returns an int, the number of bytes it accepted.
*/
request.param.write.length = (writePtr->length > maxRequestSize) ?
maxRequestSize : writePtr->length;
request.param.write.offset = writePtr->offset + amountWritten;
status = RequestResponse(pdevHandlePtr, sizeof(Pdev_Request),
&request.hdr, request.param.write.length,
writePtr->buffer + amountWritten,
sizeof(int), (Address)&numBytes,
replyPtr, waitPtr);
if (pdevHandlePtr->flags & PDEV_WRITE_BEHIND) {
/*
* Assume all bytes accepted when write-behind is enabled.
*/
numBytes = request.param.write.length;
} else if (replyPtr->length != sizeof(int)) {
numBytes = 0;
}
amountWritten += numBytes;
writePtr->length -= numBytes;
}
if (amountWritten > totalToWrite) {
printf("FspdevPseudoStreamWrite: \"%s\" amount written (%d) > requested (%d)\n",
Fsutil_HandleName(streamPtr->ioHandlePtr), amountWritten, totalToWrite);
amountWritten = totalToWrite;
}
replyPtr->length = amountWritten;
if (amountWritten > 0) {
pdevHandlePtr->ctrlHandlePtr->modifyTime = Fsutil_TimeInSeconds();
}
exit:
if (status == DEV_OFFLINE) {
/*
* Simulate a broken pipe so writers die.
*/
replyPtr->signal = SIG_PIPE;
status = FS_BROKEN_PIPE;
}
if (replyPtr->signal != 0) {
printf("PdevWrite: signal %d\n", replyPtr->signal);
}
pdevHandlePtr->flags &= ~(PDEV_BUSY|FS_USER_IN);
exitNoServer:
Sync_Broadcast(&pdevHandlePtr->access);
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* FspdevPseudoStreamIOControl --
*
* IOControls for pseudo-device. The in parameter block of the
* IOControl is passed to the server, and its response is used
* to fill the return parameter block of the client.
*
* Results:
* SUCCESS - the operation was successful.
*
* Side effects:
* None here in the kernel, anyway.
*
*----------------------------------------------------------------------
*/
ReturnStatus
FspdevPseudoStreamIOControl(streamPtr, ioctlPtr, replyPtr)
Fs_Stream *streamPtr; /* Stream to pseudo-device */
Fs_IOCParam *ioctlPtr; /* Standard I/O Control parameter block */
Fs_IOReply *replyPtr; /* Output buffer length and signal to return */
{
ReturnStatus status;
Pdev_Request request;
register Fspdev_ClientIOHandle *cltHandlePtr =
(Fspdev_ClientIOHandle *)streamPtr->ioHandlePtr;
register Fspdev_ServerIOHandle *pdevHandlePtr = cltHandlePtr->pdevHandlePtr;
Boolean sendToServer;
LOCK_MONITOR;
/*
* Wait for exclusive access to the stream.
*/
while (pdevHandlePtr->flags & PDEV_BUSY) {
if ((pdevHandlePtr->flags & PDEV_SERVER_GONE) == 0) {
(void)Sync_Wait(&pdevHandlePtr->access, FALSE);
}
if (pdevHandlePtr->flags & PDEV_SERVER_GONE) {
status = DEV_OFFLINE;
goto exit;
}
}
pdevHandlePtr->flags |= PDEV_BUSY;
/*
* Decide if the buffers are already in the kernel or not. The buffers
* for generic I/O controls are copied in, and if we are being called
* from an RPC stub they are also in kernel space.
*/
if (ioctlPtr->flags & FS_USER_IN) {
pdevHandlePtr->flags |= FS_USER_IN;
}
if (ioctlPtr->flags & FS_USER_OUT) {
pdevHandlePtr->flags |= FS_USER_OUT;
}
status = SUCCESS;
sendToServer = TRUE;
if (ioctlPtr->command == IOC_NUM_READABLE &&
pdevHandlePtr->readBuf.data != (Address)NIL) {
/*
* Trap out the IOC_NUM_READABLE if there's a read ahead buf.
*/
int bytesAvail;
if (pdevHandlePtr->flags & PDEV_READ_BUF_EMPTY) {
bytesAvail = 0;
} else {
bytesAvail = pdevHandlePtr->readBuf.lastByte -
pdevHandlePtr->readBuf.firstByte + 1;
}
if (ioctlPtr->outBufSize != sizeof(int)) {
status = GEN_INVALID_ARG;
} else if (ioctlPtr->format != mach_Format) {
int size = sizeof(int);
int inSize = sizeof(int);
int fmtStatus;
fmtStatus = Fmt_Convert("w", mach_Format, &inSize,
(Address) &bytesAvail,
ioctlPtr->format, &size, ioctlPtr->outBuffer);
if (fmtStatus != 0) {
printf("Format of ioctl failed <0x%x>\n", fmtStatus);
status = GEN_INVALID_ARG;
}
if (size != sizeof(int)) {
status = GEN_INVALID_ARG;
}
} else {
*(int *)ioctlPtr->outBuffer = bytesAvail;
}
DBG_PRINT( ("IOC %x,%x num readable %d\n",
pdevHandlePtr->hdr.fileID.major,
pdevHandlePtr->hdr.fileID.minor,
bytesAvail) );
} else {
/*
* Switch out on command for any special processing,
* then do a request-response transaction with the server.
*/
switch(ioctlPtr->command) {
case IOC_SET_OWNER: {
/*
* Siphon off the ownership so we can enforce it in the kernel.
*/
if (ioctlPtr->inBufSize < sizeof(Ioc_Owner)) {
status = GEN_INVALID_ARG;
} else {
pdevHandlePtr->ctrlHandlePtr->owner =
*(Ioc_Owner *)ioctlPtr->inBuffer;
}
break;
}
case IOC_GET_OWNER: {
/*
* Do our own get owner, but let the server see the request.
*/
if (ioctlPtr->outBufSize < sizeof(Ioc_Owner)) {
status = GEN_INVALID_ARG;
} else {
*(Ioc_Owner *)ioctlPtr->outBuffer =
pdevHandlePtr->ctrlHandlePtr->owner;
}
break;
}
case IOC_PREFIX:
status = SUCCESS;
sendToServer = FALSE;
break;
}
if (status == SUCCESS && sendToServer) {
request.hdr.operation = PDEV_IOCTL;
request.param.ioctl = *ioctlPtr;
status = RequestResponse(pdevHandlePtr, sizeof(Pdev_Request),
&request.hdr,
ioctlPtr->inBufSize, ioctlPtr->inBuffer,
ioctlPtr->outBufSize, ioctlPtr->outBuffer,
replyPtr, (Sync_RemoteWaiter *)NIL);
}
}
pdevHandlePtr->flags &= ~(PDEV_BUSY|FS_USER_IN|FS_USER_OUT);
exit:
Sync_Broadcast(&pdevHandlePtr->access);
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* FspdevPseudoStreamSelect --
*
* Select on a pseudo-device. This is done by checking the stream
* state kept on the host of the server. If the pseudo device isn't
* selectable we just return and let the server's IOC_PDEV_READY
* IOControl do the wakeup for us. (ie. we don't use the handle wait
* lists.)
*
* Results:
* SUCCESS or FS_WOULD_BLOCK
*
* Side effects:
* *outFlagsPtr modified to indicate whether the device is
* readable or writable.
*
*----------------------------------------------------------------------
*/
ReturnStatus
FspdevPseudoStreamSelect(hdrPtr, waitPtr, readPtr, writePtr, exceptPtr)
Fs_HandleHeader *hdrPtr; /* Handle on pdev to select */
Sync_RemoteWaiter *waitPtr; /* Process info for remote waiting */
int *readPtr; /* Bit to clear if non-readable */
int *writePtr; /* Bit to clear if non-writeable */
int *exceptPtr; /* Bit to clear if non-exceptable */
{
ReturnStatus status;
register Fspdev_ClientIOHandle *cltHandlePtr = (Fspdev_ClientIOHandle *)hdrPtr;
register Fspdev_ServerIOHandle *pdevHandlePtr = cltHandlePtr->pdevHandlePtr;
LOCK_MONITOR;
PDEV_TSELECT(&cltHandlePtr->hdr.fileID, *readPtr, *writePtr, *exceptPtr);
if ((pdevHandlePtr->flags & PDEV_SERVER_GONE) ||
(pdevHandlePtr->flags & PDEV_SETUP) == 0) {
status = DEV_OFFLINE;
} else {
if (*readPtr) {
if (((pdevHandlePtr->readBuf.data == (Address)NIL) &&
((pdevHandlePtr->selectBits & FS_READABLE) == 0)) ||
((pdevHandlePtr->readBuf.data != (Address)NIL) &&
(pdevHandlePtr->flags & PDEV_READ_BUF_EMPTY))) {
*readPtr = 0;
if (waitPtr != (Sync_RemoteWaiter *)NIL) {
Fsutil_FastWaitListInsert(&pdevHandlePtr->cltReadWaitList,
waitPtr);
}
}
}
if (*writePtr && ((pdevHandlePtr->selectBits & FS_WRITABLE) == 0)) {
*writePtr = 0;
if (waitPtr != (Sync_RemoteWaiter *)NIL) {
Fsutil_FastWaitListInsert(&pdevHandlePtr->cltWriteWaitList, waitPtr);
}
}
if (*exceptPtr && ((pdevHandlePtr->selectBits & FS_EXCEPTION) == 0)) {
*exceptPtr = 0;
if (waitPtr != (Sync_RemoteWaiter *)NIL) {
Fsutil_FastWaitListInsert(&pdevHandlePtr->cltExceptWaitList,waitPtr);
}
}
status = SUCCESS;
}
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* PdevClientNotify --
*
* Wakeup any processes selecting or blocking on the pseudo-stream.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
INTERNAL static void
PdevClientNotify(pdevHandlePtr)
Fspdev_ServerIOHandle *pdevHandlePtr;
{
register int selectBits = pdevHandlePtr->selectBits;
PDEV_WAKEUP(&pdevHandlePtr->hdr.fileID, pdevHandlePtr->clientPID,
pdevHandlePtr->selectBits);
if (selectBits & FS_READABLE) {
Fsutil_FastWaitListNotify(&pdevHandlePtr->cltReadWaitList);
}
if (selectBits & FS_WRITABLE) {
Fsutil_FastWaitListNotify(&pdevHandlePtr->cltWriteWaitList);
}
if (selectBits & FS_EXCEPTION) {
Fsutil_FastWaitListNotify(&pdevHandlePtr->cltExceptWaitList);
}
}
/*
*----------------------------------------------------------------------
*
* FspdevPseudoStreamCloseInt --
*
* Do a close request-response with the server.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ENTRY void
FspdevPseudoStreamCloseInt(pdevHandlePtr)
register Fspdev_ServerIOHandle *pdevHandlePtr;
{
Pdev_Request request;
LOCK_MONITOR;
while (pdevHandlePtr->flags & PDEV_BUSY) {
if ((pdevHandlePtr->flags & PDEV_SERVER_GONE) == 0) {
(void)Sync_Wait(&pdevHandlePtr->access, FALSE);
}
if (pdevHandlePtr->flags & PDEV_SERVER_GONE) {
goto exit;
}
}
pdevHandlePtr->flags |= PDEV_BUSY;
/*
* Someday we could set up a timeout call-back here in case
* the server never replies.
*/
request.hdr.operation = PDEV_CLOSE;
(void) RequestResponse(pdevHandlePtr, sizeof(Pdev_Request), &request.hdr,
0, (Address)NIL, 0, (Address)NIL, (Fs_IOReply *) NIL,
(Sync_RemoteWaiter *)NIL);
pdevHandlePtr->flags &= ~(PDEV_BUSY|FS_USER);
exit:
Sync_LockClear(&pdevHandlePtr->lock);
Sync_Broadcast(&pdevHandlePtr->access);
UNLOCK_MONITOR;
}